package com.cps.frameuploadload.schedule;

import com.alibaba.fastjson.JSON;
import com.cps.framecommon.domain.vo.LoadInfo;
import com.cps.frameuploadload.util.ToolOsInfo;
import com.sun.management.OperatingSystemMXBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.software.os.OperatingSystem;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.management.ManagementFactory;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.hyperic.sigar.CpuInfo;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;

/**
 * @author caopeisheng
 */
@Service
@Slf4j
public class UploadRedisLoadInfoSchedule {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${server.port:8080}")
    private int serverPort;
    private static com.sun.management.OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();

    @PostConstruct
    @Scheduled(cron = "${scheduledCron.uploadRedisLoadInfo}")
    public void savePrometheusRangeData() {
        String localIp4Address = ToolOsInfo.getLocalIp4Address();
        LoadInfo loadInfo = new LoadInfo();
        loadInfo.setApplicationName(applicationName);
        loadInfo.setIp(localIp4Address);
        loadInfo.setPort(serverPort);
//        loadInfo.setCpuAvailable(ToolOsInfo.getCpuAvailable());
        ToolOsInfo.setCpuInfo(loadInfo);
        loadInfo.setMemoryAvailable(ToolOsInfo.getMemoryAvailable());
        loadInfo.setTimestamp(new Timestamp(System.currentTimeMillis()));
        String redisKey = "service_load:" + applicationName + ":" + localIp4Address + "_" + serverPort;
        redisTemplate.opsForValue().set(redisKey, JSON.toJSONString(loadInfo), 30, TimeUnit.SECONDS);

        printOshiInfo();
        printOsmxbInfo();
    }

    // docker环境下获取cpu利用率和内存利用率
    private static void printOshiInfo() {
        SystemInfo systemInfo = new SystemInfo();
        CentralProcessor processor = systemInfo.getHardware().getProcessor();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;

        GlobalMemory memory = systemInfo.getHardware().getMemory();
        long totalByte = memory.getTotal();
        long availableByte = memory.getAvailable();

        log.info("Oshi:CPU = {}, processors={}, Mem = {}, totalMemory={}, freeMemory={}",
                new DecimalFormat("#.##%").format(1.0-(idle * 1.0 / totalCpu)),
                processor.getLogicalProcessorCount(),
                new DecimalFormat("#.##%").format((totalByte-availableByte)*1.0/totalByte),
                formatByte(totalByte),
                formatByte(availableByte));

    }

    public static void main(String[] args) throws SigarException, InterruptedException {
        while(true) {
            printOshiInfo();
            printOsmxbInfo();

//            Server server = new Server();
//            server.copyTo();
        }
    }




    private static void printOsmxbInfo() {
        //获取CPU
        double cpuLoad = osmxb.getSystemCpuLoad();
        //获取内存
        long totalvirtualMemory = osmxb.getTotalPhysicalMemorySize();
        long freePhysicalMemorySize = osmxb.getFreePhysicalMemorySize();
        double value = freePhysicalMemorySize / totalvirtualMemory;
        int percentMemoryLoad = (int) ((1 - value) * 100);

        int processors = Runtime.getRuntime().availableProcessors();

        log.info("Osmxb:CPU = {}, processors={}, Mem = {}, totalMemory={}, freeMemory={}",
                new DecimalFormat("#.##%").format(cpuLoad),
                processors,
                new DecimalFormat("#.##%").format((totalvirtualMemory-freePhysicalMemorySize)*1.0/totalvirtualMemory),
                formatByte(totalvirtualMemory),
                formatByte(freePhysicalMemorySize));

    }

    public static String formatByte(long byteNumber){
        double FORMAT = 1024.0;
        double kbNumber = byteNumber/FORMAT;
        if(kbNumber<FORMAT){
            return new DecimalFormat("#.##KB").format(kbNumber);
        }
        double mbNumber = kbNumber/FORMAT;
        if(mbNumber<FORMAT){
            return new DecimalFormat("#.##MB").format(mbNumber);
        }
        double gbNumber = mbNumber/FORMAT;
        if(gbNumber<FORMAT){
            return new DecimalFormat("#.##GB").format(gbNumber);
        }
        double tbNumber = gbNumber/FORMAT;
        return new DecimalFormat("#.##TB").format(tbNumber);
    }
}
